home *** CD-ROM | disk | FTP | other *** search
/ H4CK3R 14 / hacker14.iso / programacao / visual / perl.exe / {app} / Library / YAPE / Regex.pm
Encoding:
Perl POD Document  |  2001-05-03  |  25.2 KB  |  874 lines

  1. package YAPE::Regex;
  2.  
  3. use YAPE::Regex::Element;
  4. use Text::Balanced 'extract_codeblock';
  5. use Carp;
  6. use strict;
  7. use vars '$VERSION';
  8.  
  9.  
  10. $VERSION = '3.00';
  11.  
  12.  
  13. my $valid_POSIX = qr{
  14.   alpha | alnum | ascii | cntrl | digit | graph |
  15.   lower | print | punct | space | upper | word | xdigit
  16. }x;
  17.  
  18.  
  19. my $ok_cc_REx = qr{
  20.   \\([0-3][0-7]{2}) |                       # octal escapes
  21.   \\x([a-fA-F0-9]{2}|\{[a-fA-F0-9]+\}) |    # hex escapes
  22.   \\c(.) |                                  # control characters
  23.   \\([nrftbae]) |                           # known \X sequences
  24.   \\N\{([^\}]+)\} |                         # named characters
  25.   (\\[wWdDsS]) |                            # regex macros
  26.   \\[Pp]([A-Za-z]|\{[a-zA-Z]+\}) |          # utf8 macros
  27.   \[:\^?([a-zA-Z]+):\]  |                   # POSIX macros
  28.   \\?(.)                                    # anything else
  29. }xs;
  30.  
  31.  
  32. my %pat = (
  33.   Pcomment => qr{ \( \? \# ( [^)]* ) \) }x,
  34.   Xcomment => qr{ \# [^\S\n]* ( .* \n ) }x,
  35.   Pflags => qr{ \( \? ([isxm]*)-?([ismx]*) \) }x,
  36.  
  37.   Pahead => qr{ \( \? ( [=!] ) }x,
  38.   Pbehind => qr{ \( \? < ( [=!] ) }x,
  39.   Pcond => qr{ \( \? (?: \( (\d+) \) | (?= \( \? (?: <? [=!] | \?? \{ ) ) ) }x,
  40.   Pcut => qr{ \( \? > }x,
  41.   Pgroup => qr{ \( \? ([isxm]*)-?([ismx]*) : }x,
  42.   Pcapture => qr{ \( (?! \? ) }x,
  43.   Pcode => qr{ \( \? (?= \{ ) }x,
  44.   Plater => qr{ \( \? \? (?= \{ ) }x,
  45.   Pclose => qr{ \) }x,
  46.  
  47.   quant => qr{ ( (?: [+*?] | \{ \d+ ,? \d* \} ) ) }x,
  48.   ngreed => qr{ ( \? ) }x,
  49.  
  50.   anchor => qr{ ( \\ [ABbGZz] | [\^\$] ) }x,
  51.   macro => qr{ \\ ( [dDwWsS] ) }x,
  52.   oct => qr{ \\ ( [0-3] [0-7] [0-7] ) }x,
  53.   hex => qr{ \\ x ( [a-fA-F0-9]{2} ) }x,
  54.   utf8hex => qr{ \\ x \{ ( [a-fA-F0-9]+ ) \} }x,
  55.   backref => qr{ \\ ( [1-9] \d* ) }x,
  56.   ctrl => qr{ \\ c ( . ) }x,
  57.   named => qr{ \\ N \{ ( [^\}]+ ) \} }x,
  58.   Cchar => qr{ \\ C }x,
  59.   slash => qr{ \\ ( . ) }x,
  60.   any => qr{ \. }x,
  61.   class => qr{ \\ ([Pp]) ( [A-Za-z] | \{ [a-zA-Z]+ \} ) | \[ ( \^? ) ( \]? [^][\\]* (?: (?: \[:\w+:\] | \[ (?!:) | \\. ) [^][\\]* )* ) \] }x,
  62.   nws => qr{ ( (?: [^\s^\$|\\+*?()\[.]+ | \{ (?! \d+ ,? \d* \} ) )+ ) }x,
  63.   reg => qr{ ( (?: [^^\$|\\+*?()\[.\{] | \{ (?! \d+ ,? \d* \} ) )+ ) }x,
  64.  
  65.   alt => qr{ \| }x,
  66. );
  67.  
  68.  
  69. sub import {
  70.   shift;
  71.   my @obj = qw(
  72.     anchor macro oct hex utf8hex backref ctrl named Cchar slash
  73.     any class text alt comment whitespace flags lookahead lookbehind
  74.     conditional group capture code later close cut
  75.   );
  76.   no strict 'refs';
  77.   for my $class ('YAPE::Regex', @_) {
  78.     (my $file = $class . ".pm") =~ s!::!/!g;
  79.     require $file and $class->import if not $INC{$file};
  80.     if ($class ne 'YAPE::Regex') {
  81.       push @{"${class}::ISA"}, 'YAPE::Regex';
  82.       push @{"${class}::${_}::ISA"},
  83.         "YAPE::Regex::$_", "${class}::Element" for @obj;
  84.     }
  85.     push @{"${class}::${_}::ISA"}, 'YAPE::Regex::Element' for @obj;
  86.   }
  87.  
  88.  
  89. sub new {
  90.   my ($class, $regex) = @_;
  91.  
  92.   croak "no regex given to $class->new"
  93.     if not defined $regex or length($regex) == 0;
  94.  
  95.   eval { local $^W; $regex = qr/$regex/ } if ref($regex) ne 'Regexp';
  96.  
  97.   $regex = "(?-imsx:$regex)" if $@;
  98.  
  99.   my $self = bless {
  100.     TREE => [],
  101.     TREE_STACK => [],
  102.     CAPTURE => [],
  103.     CONTENT => "$regex",
  104.     DEPTH => 0,
  105.   }, $class;
  106.   $self->{CURRENT} = $self->{TREE};
  107.  
  108.   return $self;
  109. }
  110.  
  111.  
  112. sub state { $_[0]{STATE} }
  113. sub error { $_[0]{ERROR} }
  114. sub depth { $_[0]{DEPTH} }
  115. sub chunk { substr $_[0]{CONTENT}, 0, $_[1] || 30 }
  116. sub done { $_[0]{STATE} eq 'done' }
  117. sub root { $_[0]{TREE}[0] }
  118. sub top { $_[0]{TREE}[0] }
  119. sub parse { 1 while $_[0]->next }
  120. sub display { $_[0]->parse; $_[0]{TREE}[0]->fullstring if $_[0]->done;  }
  121.  
  122.  
  123. sub next {
  124.   my $self = shift;
  125.   $self->{STATE} = 'done', return unless length $self->{CONTENT};
  126.  
  127.   if (
  128.     @{$self->{TREE_STACK}} and
  129.     $self->{TREE_STACK}[-1]{MODE}{x} and
  130.     $self->{CONTENT} =~ s/^(\s+)//
  131.   ) {
  132.     my $node = (ref($self) . "::whitespace")->new($1);
  133.     push @{ $self->{CURRENT} }, $node;
  134.     return $node;
  135.   }
  136.  
  137.   if ($self->{CONTENT} =~ s/^$pat{Pcomment}//) {
  138.     my $node = (ref($self) . "::comment")->new($1,0);
  139.     push @{ $self->{CURRENT} }, $node;
  140.     $self->{STATE} = 'comment';
  141.     return $node;
  142.   }
  143.  
  144.   if (
  145.     @{ $self->{TREE_STACK} } and
  146.     $self->{TREE_STACK}[-1]{MODE}{x} and
  147.     $self->{CONTENT} =~ s/^$pat{Xcomment}//
  148.   ) {
  149.     my $node = (ref($self) . "::comment")->new($1,1);
  150.     push @{ $self->{CURRENT} }, $node;
  151.     $self->{STATE} = 'comment';
  152.     return $node;
  153.   }
  154.  
  155.   if ($self->{CONTENT} =~ s/^$pat{anchor}//) {
  156.     my $match = $1;
  157.     my ($quant,$ngreed) = $self->_get_quant;
  158.     return if $quant eq -1;
  159.     my $node = (ref($self) . "::anchor")->new($match,$quant,$ngreed);
  160.     push @{ $self->{CURRENT} }, $node;
  161.     $self->{STATE} = 'anchor';
  162.     return $node;
  163.   }
  164.  
  165.   if ($self->{CONTENT} =~ s/^$pat{macro}//) {
  166.     my $match = $1;
  167.     my ($quant,$ngreed) = $self->_get_quant;
  168.     return if $quant eq -1;
  169.     my $node = (ref($self) . "::macro")->new($match,$quant,$ngreed);
  170.     push @{ $self->{CURRENT} }, $node;
  171.     $self->{STATE} = 'macro';
  172.     return $node;
  173.   }
  174.  
  175.   if ($self->{CONTENT} =~ s/^$pat{oct}//) {
  176.     my $match = $1;
  177.     my ($quant,$ngreed) = $self->_get_quant;
  178.     return if $quant eq -1;
  179.     my $node = (ref($self) . "::oct")->new($match,$quant,$ngreed);
  180.     push @{ $self->{CURRENT} }, $node;
  181.     $self->{STATE} = 'oct';
  182.     return $node;
  183.   }
  184.  
  185.   if ($self->{CONTENT} =~ s/^$pat{hex}//) {
  186.     my $match = $1;
  187.     my ($quant,$ngreed) = $self->_get_quant;
  188.     my $node = (ref($self) . "::hex")->new($match,$quant,$ngreed);
  189.     push @{ $self->{CURRENT} }, $node;
  190.     $self->{STATE} = 'hex';
  191.     return $node;
  192.   }
  193.  
  194.   if ($self->{CONTENT} =~ s/^$pat{utf8hex}//) {
  195.     my $match = $1;
  196.     my ($quant,$ngreed) = $self->_get_quant;
  197.     my $node = (ref($self) . "::utf8hex")->new($match,$quant,$ngreed);
  198.     push @{ $self->{CURRENT} }, $node;
  199.     $self->{STATE} = 'utf8hex';
  200.     return $node;
  201.   }
  202.  
  203.   if ($self->{CONTENT} =~ s/^$pat{backref}//) {
  204.     my $match = $1;
  205.     my ($quant,$ngreed) = $self->_get_quant;
  206.     return if $quant eq -1;
  207.     my $node = (ref($self) . "::backref")->new($match,$quant,$ngreed);
  208.  
  209.   # this code is special for YAPE::Regex::Reverse
  210.   if ($self->isa('YAPE::Regex::Reverse')) {
  211.     if ($quant eq '*' or $quant eq '+') {
  212.       $node = (ref($self) . "::group")->new;
  213.       $node->{NGREED} = '?' if $quant eq '*';
  214.       $node->{CONTENT} = [
  215.         (ref($self) . "::backref")->new($match,'*'),
  216.         (ref($self) . "::backref")->new($match),
  217.       ];
  218.     }
  219.     elsif ($quant and $quant ne '?') {
  220.       my ($l,$u) = $quant =~ /(\d+),(\d*)/;
  221.       $node = (ref($self) . "::group")->new;
  222.       $node->{NGREED} = '?' if not $l;
  223.       $l-- if $l; $u-- if $u;
  224.       $node->{CONTENT} = [
  225.         (ref($self) . "::backref")->new($match,"{$l,$u}"),
  226.         (ref($self) . "::backref")->new($match),
  227.       ];
  228.     }
  229.   }
  230.     push @{ $self->{CURRENT} }, $node;
  231.     $self->{STATE} = 'backref';
  232.     return $node;
  233.   }
  234.  
  235.   if ($self->{CONTENT} =~ s/^$pat{ctrl}//) {
  236.     my $match = $1;
  237.     my ($quant,$ngreed) = $self->_get_quant;
  238.     return if $quant eq -1;
  239.     my $node = (ref($self) . "::ctrl")->new($match,$quant,$ngreed);
  240.     push @{ $self->{CURRENT} }, $node;
  241.     $self->{STATE} = 'ctrl';
  242.     return $node;
  243.   }
  244.  
  245.   if ($self->{CONTENT} =~ s/^$pat{named}//) {
  246.     my $match = $1;
  247.     my ($quant,$ngreed) = $self->_get_quant;
  248.     return if $quant eq -1;
  249.     my $node = (ref($self) . "::named")->new($match,$quant,$ngreed);
  250.     push @{ $self->{CURRENT} }, $node;
  251.     $self->{STATE} = 'named';
  252.     return $node;
  253.   }
  254.  
  255.   if ($self->{CONTENT} =~ s/^$pat{Cchar}//) {
  256.     my ($quant,$ngreed) = $self->_get_quant;
  257.     return if $quant eq -1;
  258.     my $node = (ref($self) . "::Cchar")->new($quant,$ngreed);
  259.     push @{ $self->{CURRENT} }, $node;
  260.     $self->{STATE} = 'Cchar';
  261.     return $node;
  262.   }
  263.  
  264.   if ($self->{CONTENT} =~ s/^$pat{class}//) {
  265.     my ($neg,$match) = defined($1) ? ($1,$2) : ($3,$4);
  266.     $match =~ tr/{}//d if defined $1;
  267.         
  268.     my ($quant,$ngreed) = $self->_get_quant;
  269.     return if $quant eq -1;
  270.     return unless $self->_ok_class($match);
  271.     my $node = (ref($self) . "::class")->new($match,$neg,$quant,$ngreed);
  272.     push @{ $self->{CURRENT} }, $node;
  273.     $self->{STATE} = 'class';
  274.     return $node;
  275.   }
  276.  
  277.   if ($self->{CONTENT} =~ s/^$pat{slash}//) {
  278.     my $match = $1;
  279.     my ($quant,$ngreed) = $self->_get_quant;
  280.     return if $quant eq -1;
  281.     my $node = (ref($self) . "::slash")->new($match,$quant,$ngreed);
  282.     push @{ $self->{CURRENT} }, $node;
  283.     $self->{STATE} = 'slash';
  284.     return $node;
  285.   }
  286.  
  287.   if ($self->{CONTENT} =~ s/^$pat{any}//) {
  288.     my ($quant,$ngreed) = $self->_get_quant;
  289.     return if $quant eq -1;
  290.     my $node = (ref($self) . "::any")->new($quant,$ngreed);
  291.     push @{ $self->{CURRENT} }, $node;
  292.     $self->{STATE} = 'any';
  293.     return $node;
  294.   }
  295.  
  296.   if (
  297.     @{ $self->{TREE_STACK} } and
  298.     $self->{TREE_STACK}[-1]{MODE}{x} and
  299.     $self->{CONTENT} =~ s/^$pat{nws}//
  300.   ) {
  301.     my $match = $1;
  302.     my ($quant,$ngreed) = $self->_get_quant;
  303.     my $node = (ref($self) . "::text")->new($match,$quant,$ngreed);
  304.     push @{ $self->{CURRENT} }, $node;
  305.     $self->{STATE} = 'text';
  306.     return $node;
  307.   }
  308.  
  309.   if ($self->{CONTENT} =~ s/^$pat{reg}//) {
  310.     my $match = $1;
  311.     my $node;
  312.     if (length($match) > 1 and $self->{CONTENT} =~ /^$pat{quant}/) {
  313.       $self->{CONTENT} = chop($match) . $self->{CONTENT};
  314.       $node = (ref($self) . "::text")->new($match,"","");
  315.     }
  316.     else {
  317.       my ($quant,$ngreed) = $self->_get_quant;
  318.       return if $quant eq -1;
  319.       $node = (ref($self) . "::text")->new($match,$quant,$ngreed);
  320.     }      
  321.     push @{ $self->{CURRENT} }, $node;
  322.     $self->{STATE} = 'text';
  323.     return $node;
  324.   }
  325.  
  326.   if ($self->{CONTENT} =~ s/^$pat{alt}//) {{
  327.     if (
  328.       @{ $self->{TREE_STACK} } and
  329.       $self->{TREE_STACK}[-1]->type eq 'cond' and
  330.       $self->{TREE_STACK}[-1]{OPTS} == 2
  331.     ) {
  332.       $self->{CONTENT} = '|' . $self->{CONTENT};
  333.       last;
  334.     }
  335.     my $node = (ref($self) . "::alt")->new;
  336.     if (
  337.       @{ $self->{TREE_STACK} } and
  338.       $self->{TREE_STACK}[-1]->type eq 'cond'
  339.     ) {
  340.       $self->{TREE_STACK}[-1]{OPTS}++;
  341.       $self->{CURRENT} = $self->{TREE_STACK}[-1]{FALSE};
  342.     }
  343.     else {
  344.       push @{ $self->{CURRENT} }, $node;
  345.     }
  346.     $self->{STATE} = 'alt';
  347.     return $node;
  348.   }}
  349.  
  350.   if ($self->{CONTENT} =~ s/^$pat{Pflags}//) {
  351.     my ($add,$sub) = ($1,$2);
  352.     my $mode = $self->{TREE_STACK}[-1]{MODE};
  353.     @{$mode}{split //, $add} = (1) x length($add);
  354.     delete @{$mode}{split //, $sub};
  355.     my $node = (ref($self) . "::flags")->new($add,$sub);
  356.     push @{ $self->{CURRENT} }, $node;
  357.     $self->{STATE} = 'flags';
  358.     return $node;
  359.   }
  360.  
  361.   if ($self->{CONTENT} =~ s/^$pat{Pcond}//) {
  362.     if (defined $1) {
  363.       my $node = (ref($self) . "::conditional")->new($1);
  364.       $node->{MODE} = { %{ $self->{TREE_STACK}[-1]{MODE} } } if
  365.           @{ $self->{TREE_STACK} };
  366.       push @{ $self->{TREE_STACK} }, $node;
  367.       push @{ $self->{CURRENT} }, $node;
  368.       $self->{CURRENT} = $node->{TRUE};
  369.       $self->{DEPTH}++;
  370.       $self->{STATE} = "cond($1)";
  371.       return $node;
  372.     }
  373.     else {
  374.       my $node = (ref($self) . "::conditional")->new;
  375.       $node->{MODE} = { %{ $self->{TREE_STACK}[-1]{MODE} } } if
  376.           @{ $self->{TREE_STACK} };
  377.       push @{ $self->{TREE_STACK} }, $node;
  378.       push @{ $self->{CURRENT} }, $node;
  379.       $self->{CURRENT} = $node->{CONTENT};
  380.       $self->{DEPTH}++;
  381.       $self->{STATE} = "cond(assert)";
  382.       return $node;
  383.     }
  384.   }
  385.  
  386.   if ($self->{CONTENT} =~ s/^$pat{Pcut}//) {
  387.     my ($quant,$ngreed) = $self->_get_quant;
  388.     return if $quant eq -1;
  389.     my $node = (ref($self) . "::cut")->new([],$quant,$ngreed);
  390.     $node->{MODE} = { %{ $self->{TREE_STACK}[-1]{MODE} } };
  391.     push @{ $self->{TREE_STACK} }, $node;
  392.     push @{ $self->{CURRENT} }, $node;
  393.     $self->{CURRENT} = $node->{CONTENT};
  394.     $self->{DEPTH}++;
  395.     $self->{STATE} = 'cut';
  396.     return $node;
  397.   }
  398.  
  399.   if ($self->{CONTENT} =~ s/^$pat{Pahead}//) {
  400.     my $node = (ref($self) . "::lookahead")->new($1 eq '=' ? 1 : 0);
  401.     $node->{MODE} = { %{ $self->{TREE_STACK}[-1]{MODE} } };
  402.     push @{ $self->{TREE_STACK} }, $node;
  403.     push @{ $self->{CURRENT} }, $node;
  404.     $self->{CURRENT} = $node->{CONTENT};
  405.     $self->{DEPTH}++;
  406.     $self->{STATE} = 'lookahead(' . ('neg','pos')[$1 eq '='] . ')';
  407.     return $node;
  408.   }
  409.  
  410.   if ($self->{CONTENT} =~ s/^$pat{Pbehind}//) {
  411.     my $node = (ref($self) . "::lookbehind")->new($1 eq '=' ? 1 : 0);
  412.     $node->{MODE} = { %{ $self->{TREE_STACK}[-1]{MODE} } };
  413.     push @{ $self->{TREE_STACK} }, $node;
  414.     push @{ $self->{CURRENT} }, $node;
  415.     $self->{CURRENT} = $node->{CONTENT};
  416.     $self->{DEPTH}++;
  417.     $self->{STATE} = 'lookbehind(' . ('neg','pos')[$1 eq '='] . ')';
  418.     return $node;
  419.   }
  420.  
  421.   if ($self->{CONTENT} =~ s/^$pat{Pgroup}//) {
  422.     my ($add,$sub) = ($1,$2);
  423.     my $node = (ref($self) . "::group")->new($add || "",$sub || "");
  424.     $node->{MODE} = { %{ $self->{TREE_STACK}[-1]{MODE} } } if
  425.       @{ $self->{TREE_STACK} };
  426.     @{$node->{MODE}}{split //, $add} = (1) x length($add);
  427.     delete @{$node->{MODE}}{split //, $sub};
  428.     push @{ $self->{TREE_STACK} }, $node;
  429.     push @{ $self->{CURRENT} }, $node;
  430.     $self->{CURRENT} = $node->{CONTENT};
  431.     $self->{DEPTH}++;
  432.     $self->{STATE} = 'group';
  433.     return $node;
  434.   }
  435.  
  436.   if ($self->{CONTENT} =~ s/^$pat{Pcapture}//) {
  437.     my $node = (ref($self) . "::capture")->new;
  438.     $node->{MODE} = { %{ $self->{TREE_STACK}[-1]{MODE} } } if
  439.       @{ $self->{TREE_STACK} };
  440.     push @{ $self->{TREE_STACK} }, $node;
  441.     push @{ $self->{CURRENT} }, $node;
  442.     push @{ $self->{CAPTURE} }, $node;
  443.     $self->{CURRENT} = $node->{CONTENT};
  444.     $self->{DEPTH}++;
  445.     $self->{STATE} = 'capture(' . @{ $self->{CAPTURE} } . ')';
  446.     return $node;
  447.   }
  448.  
  449.   if ($self->{CONTENT} =~ s/^$pat{Pcode}//) {
  450.     my ($code,$left) = extract_codeblock($self->{CONTENT}) or do {
  451.       $self->{ERROR} = 'bad code in (?{ ... }) assertion';
  452.       $self->{STATE} = 'error';
  453.       return;
  454.     };
  455.     
  456.     $self->{CONTENT} = $left;
  457.     my $node = (ref($self) . "::code")->new($code);
  458.     push @{ $self->{TREE_STACK} }, $node;
  459.     push @{ $self->{CURRENT} }, $node;
  460.     $self->{DEPTH}++;
  461.     $self->{STATE} = 'code';
  462.     return $node;
  463.   }
  464.   
  465.   if ($self->{CONTENT} =~ s/^$pat{Plater}//) {
  466.     my ($code,$left) = extract_codeblock($self->{CONTENT}) or do {
  467.       $self->{ERROR} = 'bad code in (??{ ... }) assertion';
  468.       $self->{STATE} = 'error';
  469.       return;
  470.     };
  471.     
  472.     $self->{CONTENT} = $left;
  473.     my $node = (ref($self) . "::later")->new($code);
  474.     push @{ $self->{TREE_STACK} }, $node;
  475.     push @{ $self->{CURRENT} }, $node;
  476.     $self->{DEPTH}++;
  477.     $self->{STATE} = 'later';
  478.     return $node;
  479.   }
  480.   
  481.   if ($self->{DEPTH}-- and $self->{CONTENT} =~ s/^$pat{Pclose}//) {
  482.     my ($quant,$ngreed) = $self->_get_quant;
  483.     return if $quant eq -1;
  484.     my $node = (ref($self) . "::close")->new;
  485.     
  486.     $self->{CURRENT} = pop @{ $self->{TREE_STACK} };
  487.     $self->{CURRENT}{QUANT} = $quant;
  488.     $self->{CURRENT}{NGREED} = $ngreed;
  489.  
  490.   # this code is special to YAPE::Regex::Reverse
  491.   if ($self->isa('YAPE::Regex::Reverse')) {
  492.     if ($quant eq '*' or $quant eq '+') {
  493.       my $old = $self->{CURRENT}{CONTENT};
  494.       $self->{CURRENT}{CONTENT} = [
  495.         (ref($self) . "::group")->new,
  496.         (ref($self) . "::capture")->new,
  497.       ];
  498.       $self->{CURRENT}{NGREED} = '?' if $quant eq '*';
  499.       $self->{CURRENT}{CONTENT}[0]{CONTENT} = $old;
  500.       $self->{CURRENT}{CONTENT}[0]{QUANT} = '*';
  501.       $self->{CURRENT}{CONTENT}[1]{CONTENT} = $old;
  502.       $self->{CAPTURE}[-1] = $self->{CURRENT}{CONTENT}[1];
  503.       bless $self->{CURRENT}, (ref($self) . '::group');
  504.     }
  505.     elsif ($quant and $quant ne '?') {
  506.       my ($l,$u) = $quant =~ /(\d+),(\d*)/;
  507.       my $old = $self->{CURRENT}{CONTENT};
  508.       $self->{CURRENT}{CONTENT} = [
  509.         (ref($self) . "::group")->new,
  510.         (ref($self) . "::capture")->new,
  511.       ];
  512.       $self->{CURRENT}{NGREED} = '?' if not $l;
  513.       $l-- if $l; $u-- if $u;
  514.       $self->{CURRENT}{CONTENT}[0]{CONTENT} = $old;
  515.       $self->{CURRENT}{CONTENT}[0]{QUANT} = "{$l,$u}";
  516.       $self->{CURRENT}{CONTENT}[1]{CONTENT} = $old;
  517.       $self->{CAPTURE}[-1] = $self->{CURRENT}{CONTENT}[1];
  518.       bless $self->{CURRENT}, (ref($self) . '::group');
  519.     }
  520.   }
  521.  
  522.     if (
  523.       @{ $self->{TREE_STACK} } and
  524.       $self->{TREE_STACK}[-1]->type eq 'cond' and
  525.       $self->{TREE_STACK}[-1]{OPTS} == 1
  526.     ) {
  527.       $self->{CURRENT} = $self->{TREE_STACK}[-1]{TRUE};
  528.     }
  529.     else {
  530.       $self->{CURRENT} = $self->{TREE_STACK}[-1];
  531.       $self->{CURRENT} = $self->{CURRENT}{CONTENT};
  532.     }
  533.     
  534.     $self->{STATE} = 'close';
  535.     return $node;
  536.   }
  537.  
  538.   my $token = $self->chunk(1);
  539.   $self->{ERROR} = "unexpected token '$token' during '$self->{STATE}'";
  540.   $self->{STATE} = 'error';
  541.  
  542.   return;
  543. }
  544.  
  545.  
  546. sub extract {
  547.   my $self = shift;
  548.   $self->parse;
  549.   
  550.   my @nodes = @{ $self->{CAPTURE} };
  551.   
  552.   return sub { shift @nodes };
  553. }
  554.  
  555.  
  556. sub _get_quant {
  557.   my $self = shift;
  558.   my ($quant,$ngreed) = ("","");
  559.  
  560.   if (
  561.     $self->{CONTENT} =~ s/^($pat{Pcomment})?$pat{quant}// or
  562.     (@{ $self->{TREE_STACK} } and $self->{TREE_STACK}[-1]{MODE}{x} and
  563.       $self->{CONTENT} =~ s/^($pat{Xcomment}?\s*)?$pat{quant}//)
  564.   ) {
  565.     $quant = $+;
  566.     {
  567.       if ($quant =~ /^\{(\d+),(\d+)\}/ and $1 > $2) {
  568.         $self->{ERROR} = "upper bound lower than lower bound ($quant)";
  569.         $self->{STATE} = 'error';
  570.         return -1;
  571.       }
  572.     }
  573.     $self->{CONTENT} = $1 . $self->{CONTENT} if $1;
  574.   }
  575.  
  576.   my ($ws) = $1 if
  577.     @{ $self->{TREE_STACK} } and
  578.     $self->{TREE_STACK}[-1]{MODE}{x} and
  579.     $self->{CONTENT} =~ s/^(\s+)//;
  580.  
  581.   if (
  582.     (@{ $self->{TREE_STACK} } and $self->{TREE_STACK}[-1]{MODE}{x} and
  583.       $self->{CONTENT} =~ s/^($pat{Xcomment}?\s*)?$pat{ngreed}//) or
  584.       $self->{CONTENT} =~ s/^($pat{Pcomment})?$pat{ngreed}//
  585.   ) {
  586.     $ngreed = $+;
  587.     $self->{CONTENT} = $1 . $self->{CONTENT} if $1;
  588.   }
  589.  
  590.   $self->{CONTENT} = $ws . $self->{CONTENT} if $ws;
  591.  
  592.   return ($quant,$ngreed);
  593. }
  594.  
  595.  
  596. sub _ok_class {
  597.   my ($self,$class) = @_;
  598.  
  599.   while ($class =~ s/^($ok_cc_REx)//) {
  600.     my $c1 = $1;
  601.  
  602.     my $a =
  603.       defined($2) ? oct($2) :
  604.       defined($3) ? hex(($3 =~ /(\w+)/)[0]) :
  605.       defined($4) ? ord($4) - 64 :
  606.       defined($5) ? ord(eval qq{"\\$5"}) :
  607.       defined($6) ? ord(eval qq{use charnames ':full'; "\\N{$6}"}) :
  608.       defined($10) ? ord($10) :
  609.                     -1;
  610.  
  611.     my ($utf8,$posix) = ($8,$9);
  612.     
  613.     $utf8 =~ tr/{}//d if defined $utf8;
  614.  
  615.     if (defined($posix) and $posix !~ $valid_POSIX) {
  616.       $self->{ERROR} = "unknown POSIX class $c1";
  617.       $self->{STATE} = 'error';
  618.       return;
  619.     }
  620.  
  621.     if ($class =~ s/^-($ok_cc_REx)//) {
  622.       my $c2 = $1;
  623.       my $b =
  624.         defined($2) ? oct($2) :
  625.         defined($3) ? hex(($3 =~ /(\w+)/)[0]) :
  626.         defined($4) ? ord($4) - 64 :
  627.         defined($5) ? ord(eval qq{"\\$5"}) :
  628.         defined($6) ? ord(eval qq{use charnames ':full'; "\\N{$6}"}) :
  629.         defined($10) ? ord($10) :
  630.                       -1;
  631.  
  632.       my ($utf8,$posix) = ($8,$9);
  633.       
  634.       $utf8 =~ tr/{}//d if defined $utf8;
  635.   
  636.       if (defined($posix) and $posix !~ $valid_POSIX) {
  637.         $self->{ERROR} = "unknown POSIX class $c2";
  638.         $self->{STATE} = 'error';
  639.         return;
  640.       }
  641.   
  642.       if ($a == -1 or $b == -1) {
  643.         carp qq{false [] range "$c1-$c2"} if $^W;
  644.       }
  645.       elsif ($a > $b) {
  646.         $self->{ERROR} = "invalid [] range $c1-$c2";
  647.         $self->{STATE} = 'error';
  648.         return;
  649.       }
  650.     }
  651.   }
  652.  
  653.   return 1;
  654. }
  655.  
  656.  
  657. 1;
  658.  
  659. __END__
  660.  
  661. =head1 NAME
  662.  
  663. YAPE::Regex - Yet Another Parser/Extractor for Regular Expressions
  664.  
  665. =head1 SYNOPSIS
  666.  
  667.   use YAPE::Regex;
  668.   use strict;
  669.   
  670.   my $regex = qr/reg(ular\s+)?exp?(ression)?/i;
  671.   my $parser = YAPE::Regex->new($regex);
  672.   
  673.   # here is the tokenizing part
  674.   while (my $chunk = $parser->next) {
  675.     # ...
  676.   }
  677.  
  678. =head1 C<YAPE> MODULES
  679.  
  680. The C<YAPE> hierarchy of modules is an attempt at a unified means of parsing
  681. and extracting content.  It attempts to maintain a generic interface, to
  682. promote simplicity and reusability.  The API is powerful, yet simple.  The
  683. modules do tokenization (which can be intercepted) and build trees, so that
  684. extraction of specific nodes is doable.
  685.  
  686. =head1 DESCRIPTION
  687.  
  688. This module is yet another (?) parser and tree-builder for Perl regular
  689. expressions.  It builds a tree out of a regex, but at the moment, the extent of
  690. the extraction tool for the tree is quite limited (see L<Extracting Sections>).
  691. However, the tree can be useful to extension modules.
  692.  
  693. =head1 USAGE
  694.  
  695. In addition to the base class, C<YAPE::Regex>, there is the auxiliary class
  696. C<YAPE::Regex::Element> (common to all C<YAPE> base classes) that holds the
  697. individual nodes' classes.  There is documentation for the node classes in
  698. that module's documentation.
  699.  
  700. =head2 Methods for C<YAPE::Regex>
  701.  
  702. =over 4
  703.  
  704. =item * C<use YAPE::Regex;>
  705.  
  706. =item * C<use YAPE::Regex qw( MyExt::Mod );>
  707.  
  708. If supplied no arguments, the module is loaded normally, and the node classes
  709. are given the proper inheritence (from C<YAPE::Regex::Element>).  If you supply
  710. a module (or list of modules), C<import> will automatically include them (if
  711. needed) and set up I<their> node classes with the proper inheritence -- that is,
  712. it will append C<YAPE::Regex> to C<@MyExt::Mod::ISA>, and C<YAPE::Regex::xxx>
  713. to each node class's C<@ISA> (where C<xxx> is the name of the specific node
  714. class).
  715.  
  716.   package MyExt::Mod;
  717.   use YAPE::Regex 'MyExt::Mod';
  718.   
  719.   # does the work of:
  720.   # @MyExt::Mod::ISA = 'YAPE::Regex'
  721.   # @MyExt::Mod::text::ISA = 'YAPE::Regex::text'
  722.   # ...
  723.  
  724. =item * C<my $p = YAPE::Regex-E<gt>new($REx);>
  725.  
  726. Creates a C<YAPE::Regex> object, using the contents of C<$REx> as a regular
  727. expression.  The C<new> method will I<attempt> to convert C<$REx> to a compiled
  728. regex (using C<qr//>) if C<$REx> isn't already one.  If there is an error in
  729. the regex, this will fail, but the parser will pretend it was ok.  It will then
  730. report the bad token when it gets to it, in the course of parsing.
  731.  
  732. =item * C<my $text = $p-E<gt>chunk($len);>
  733.  
  734. Returns the next C<$len> characters in the input string; C<$len> defaults to
  735. 30 characters.  This is useful for figuring out why a parsing error occurs.
  736.  
  737. =item * C<my $done = $p-E<gt>done;>
  738.  
  739. Returns true if the parser is done with the input string, and false otherwise.
  740.  
  741. =item * C<my $errstr = $p-E<gt>error;>
  742.  
  743. Returns the parser error message.
  744.  
  745. =item * C<my $backref = $p-E<gt>extract;>
  746.  
  747. Returns a code reference that returns the next back-reference in the regex.
  748. For more information on enhancements in upcoming versions of this module, check
  749. L<Extracting Sections>.
  750.  
  751. =item * C<my $node = $p-E<gt>display(...);>
  752.  
  753. Returns a string representation of the entire content.  It calls the C<parse>
  754. method in case there is more data that has not yet been parsed.  This calls the
  755. C<fullstring> method on the root nodes.  Check the C<YAPE::Regex::Element> docs
  756. on the arguments to C<fullstring>.
  757.  
  758. =item * C<my $node = $p-E<gt>next;>
  759.  
  760. Returns the next token, or C<undef> if there is no valid token.  There will be
  761. an error message (accessible with the C<error> method) if there was a problem in
  762. the parsing.
  763.  
  764. =item * C<my $node = $p-E<gt>parse;>
  765.  
  766. Calls C<next> until all the data has been parsed.
  767.  
  768. =item * C<my $node = $p-E<gt>root;>
  769.  
  770. Returns the root node of the tree structure.
  771.  
  772. =item * C<my $state = $p-E<gt>state;>
  773.  
  774. Returns the current state of the parser.  It is one of the following values:
  775. C<alt>, C<anchor>, C<any>, C<backref>, C<capture(N)>, C<Cchar>, C<class>, C<close>,
  776. C<code>, C<comment>, C<cond(TYPE)>, C<ctrl>, C<cut>, C<done>, C<error>, C<flags>,
  777. C<group>, C<hex>, C<later>, C<lookahead(neg|pos)>, C<lookbehind(neg|pos)>,
  778. C<macro>, C<named>, C<oct>, C<slash>, C<text>, and C<utf8hex>.
  779.  
  780. For C<capture(N)>, I<N> will be the number the captured pattern represents.
  781.  
  782. For C<cond(TYPE)>, I<TYPE> will either be a number representing the
  783. back-reference that the conditional depends on, or the string C<assert>.
  784.  
  785. For C<lookahead> and C<lookbehind>, one of C<neg> and C<pos> will be there,
  786. depending on the type of assertion.
  787.  
  788. =item * C<my $node = $p-E<gt>top;>
  789.  
  790. Synonymous to C<root>.
  791.  
  792. =back
  793.  
  794. =head2 Extracting Sections
  795.  
  796. While extraction of nodes is the goal of the C<YAPE> modules, the author is at
  797. a loss for words as to what needs to be extracted from a regex.  At the current
  798. time, all the C<extract> method does is allow you access to the regex's set of
  799. back-references:
  800.  
  801.   my $extor = $parser->extract;
  802.   while (my $backref = $extor->()) {
  803.     # ...
  804.   }
  805.  
  806. C<japhy> is very open to suggestions as to the approach to node extraction (in
  807. how the API should look, in addition to what should be proffered).  Preliminary
  808. ideas include extraction keywords like the output of B<-Dr> (or the C<re>
  809. module's C<debug> option).
  810.  
  811. =head1 EXTENSIONS
  812.  
  813. =over 4
  814.  
  815. =item * C<YAPE::Regex::Explain> 3.00
  816.  
  817. Presents an explanation of a regular expression, node by node.
  818.  
  819. =item * C<YAPE::Regex::Reverse> (Not released)
  820.  
  821. Reverses the nodes of a regular expression.
  822.  
  823. =back
  824.  
  825. =head1 TO DO
  826.  
  827. This is a listing of things to add to future versions of this module.
  828.  
  829. =head2 API
  830.  
  831. =over 4
  832.  
  833. =item * Create a robust C<extract> method
  834.  
  835. Open to suggestions.
  836.  
  837. =back
  838.  
  839. =head1 BUGS
  840.  
  841. Following is a list of known or reported bugs.
  842.  
  843. =head2 Pending
  844.  
  845. =over 4
  846.  
  847. =item * C<use charnames ':full'>
  848.  
  849. To understand C<\N{...}> properly, you must be using 5.6.0 or higher.  However,
  850. the parser only knows how to resolve full names (those made using C<use charnames
  851. ':full'>).  There might be an option in the future to specify a class name.
  852.  
  853. =back
  854.  
  855. =head1 SUPPORT
  856.  
  857. Visit C<YAPE>'s web site at F<http://www.pobox.com/~japhy/YAPE/>.
  858.  
  859. =head1 SEE ALSO
  860.  
  861. The C<YAPE::Regex::Element> documentation, for information on the node classes.
  862. Also, C<Text::Balanced>, Damian Conway's excellent module, used for the matching
  863. of C<(?{ ... })> and C<(??{ ... })> blocks.
  864.  
  865. =head1 AUTHOR
  866.  
  867.   Jeff "japhy" Pinyan
  868.   CPAN ID: PINYAN
  869.   japhy@pobox.com
  870.   http://www.pobox.com/~japhy/
  871.  
  872. =cut
  873.